package com.apobates.forum.grief.concurrent;

import com.apobates.forum.grief.Commons;
import java.io.Serializable;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 值对象的异步组合模板
 * @param <T> 值对象的类型
 * @param <R> 目标对象的类型
 */
public class VoCombineAsyncTemplate<T,R> implements Serializable {
    /**
     * 设置值对象,例: TopicVo
     * @param tClass 值对象的Class, 例: TopicVo.class
     * @param <T> 值对象的类型
     * @param <R> 目标对象的类型
     * @return
     */
    public static<T,R> VoCombineAsyncTemplate template(Class<T> tClass) throws IllegalStateException {
        T t = Commons.getInstance(tClass).orElseThrow(()->new IllegalStateException("实例化对象失败"));
        return new VoCombineAsyncTemplate<>(t);
    }

    //值对象的目标对象,例: Topic
    private final R target;
    //
    private CombineResult<T> result;

    private VoCombineAsyncTemplate(T ins) {
        this.target = null;
        this.result = new CombineResult<>(ins);
    }

    private VoCombineAsyncTemplate(CombineResult<T> result, R target){
        this.target = target;
        this.result = result;
    }

    /**
     * 设置目标实例,例: Topic; 目标实例在值对象的属性名使用Class.SimpleName
     * @param targetIns 目标实例
     * @return
     */
    public VoCombineAsyncTemplate<T,R> with(R targetIns){
        CombineResult<T> tmp = this.result.target(getAttrName(targetIns.getClass()), targetIns);
        return new VoCombineAsyncTemplate<>(tmp, targetIns);
    }

    /**
     * 设置目标实例,例: Topic
     * @param attributeName 目标实例在值对象的属性名
     * @param targetIns 目标实例
     * @return
     */
    public VoCombineAsyncTemplate<T,R> with(String attributeName, R targetIns){
        CombineResult<T> tmp = this.result.target(attributeName, targetIns);
        return new VoCombineAsyncTemplate<>(tmp, targetIns);
    }

    /**
     * 设置组合对象,若组合对象映射后若不用(Optional.isPresent==false)时为null
     * @param attributeName 组合对象在值对象中的属性名
     * @param mapper 组合对象的映射函数
     * @param <P> 组合对象的类型
     * @return
     */
    public <P> VoCombineAsyncTemplate<T,R> combine(final String attributeName, Function<R, CompletableFuture<Optional<P>>> mapper){
        return this.combine(attributeName, mapper, null);
    }
    /**
     * 设置组合对象,若组合对象映射后若不用(Optional.isPresent==false)时为null
     * @param attributeName 组合对象在值对象中的属性名
     * @param mapper 组合对象的映射函数
     * @param <P> 组合对象的类型
     * @return
     */
    public <P> VoCombineAsyncTemplate<T,R> combineAsync(final String attributeName, Function<R, Supplier<Optional<P>>> mapper){
        return this.combineAsync(attributeName, mapper, null);
    }
    /**
     * 设置组合对象
     * @param attributeName 组合对象在值对象中的属性名
     * @param mapper 组合对象的映射函数
     * @param defaultInstance 组合对象映射后若不用(Optional.isPresent==false)时的默认值
     * @param <P> 组合对象的类型
     * @return
     */
    public <P> VoCombineAsyncTemplate<T,R> combine(final String attributeName, Function<R, CompletableFuture<Optional<P>>> mapper, P defaultInstance){
        CombineResult<T> tmp = this.result.plug(
                attributeName,
                mapper.apply(this.target).completeOnTimeout(Optional.empty(), 1, TimeUnit.SECONDS),
                defaultInstance);
        return new VoCombineAsyncTemplate<>(tmp, this.target);
    }
    /**
     * 设置组合对象
     * @param attributeName 组合对象在值对象中的属性名
     * @param mapper 组合对象的映射函数
     * @param defaultInstance 组合对象映射后若不用(Optional.isPresent==false)时的默认值
     * @param <P> 组合对象的类型
     * @return
     */
    public <P> VoCombineAsyncTemplate<T,R> combineAsync(final String attributeName, Function<R, Supplier<Optional<P>>> mapper, P defaultInstance){
        CombineResult<T> tmp = this.result.plug(
                attributeName,
                CompletableFuture.supplyAsync(mapper.apply(this.target)).completeOnTimeout(Optional.empty(), 1, TimeUnit.SECONDS),
                defaultInstance);
        return new VoCombineAsyncTemplate<>(tmp, this.target);
    }
    /**
     * 返回最终的值对象
     * @return
     */
    public Optional<T> toInstance(){
        return this.result.getResult();
    }

    private static class CombineResult<T>{
        private CompletableFuture<T> ins;

        public CombineResult(T ins) {
            this.ins = CompletableFuture.supplyAsync(()->ins);
        }

        private CombineResult(CompletableFuture<T> insFuture) {
            this.ins = insFuture;
        }

        public <R> CombineResult<T> target(final String targetAttrName, final R targetIns){
            CompletableFuture<T> tmpIns = this.ins.thenApply(tmpVoIns->{
                setAttrVal(tmpVoIns, targetAttrName, targetIns);
                return tmpVoIns;
            });
            return new CombineResult<>(tmpIns);
        }

        public <P> CombineResult<T> plug(final String attributeName, final CompletableFuture<Optional<P>> action, final P defaultIns) {
            CompletableFuture<T> tmpIns = action.thenCompose(extInsOpt->{
                return this.ins.thenApply(tmpVoIns->{
                    setAttrVal(tmpVoIns, attributeName, extInsOpt.orElse(defaultIns));
                    return tmpVoIns;
                });
            });
            return new CombineResult(tmpIns);
        }

        public Optional<T> getResult(){
            return this.ins.thenApply(tmpVoIns->Optional.ofNullable(tmpVoIns)).completeOnTimeout(Optional.empty(), 1, TimeUnit.SECONDS).join();
        }
    }

    private static String getAttrName(Class<?> cls){
        return cls.getSimpleName().toLowerCase();
    }

    private static void setAttrVal(Object obj, String attrName, Object attrVal){
        try{
            Commons.setAttrValByReflectField(obj, attrName, attrVal);
        }catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e){
            e.printStackTrace();
        }
    }
}
